#include <buola/buola.h>

#include "ctorrentmanager.h"
#include "ctorrent.h"

#include <libtorrent/session.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/session_status.hpp>
#include <libtorrent/alert_types.hpp>

#include <buola/io/cfile.h>
#include <buola/app/ctimer.h>
#include <buola/io.h>

#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"

namespace buola { namespace bitdaemon {

//////////////////////////////////////////////////
///////////////// CTorrentManager ////////////////
//////////////////////////////////////////////////

CTorrentManager::CTorrentManager()
{
}

CTorrentManager::~CTorrentManager()
{
}

void CTorrentManager::InitSession()
{
    if(mSession) return;

    mSession.reset(new libtorrent::session(libtorrent::fingerprint("BU",1,0,0,0)));
    
    libtorrent::session_settings lSettings;
    lSettings.upload_rate_limit=100000;
    lSettings.download_rate_limit=1000000;
    lSettings.unchoke_slots_limit=8;
    lSettings.connections_limit=200;
    
    mSession->set_settings(lSettings);
    boost::system::error_code lCode;
    mSession->listen_on(std::make_pair(6900,6999),lCode);
    
    if(!mSession->is_listening())
    {
        msg_error() << "couldn't listen in any port\n";
    }

    mSession->set_alert_mask(libtorrent::alert::all_categories);

    mAlertTimer.reset(new CTimer(chrono::milliseconds(200),true));
    mAlertTimer->sTimer.Connect(&CTorrentManager::OnAlertTimer,this);
}

void CTorrentManager::AddTorrent(const io::CURI &pURI,const io::CURI &pDestination)
{
    if(!mSession) InitSession();
    
    libtorrent::add_torrent_params lParams;
    lParams.save_path=pDestination.Path();

    if(pURI.SchemeName()=="magnet")
    {
        lParams.url=pURI.ToString();
    }
    else //try to open torrent file
    {
        io::CFile lFile(pURI);
        io::PStream lStream=lFile.GetStream(io::EMode::READ);
        std::size_t lSize=lFile.Size();
        std::vector<char> lContents(lSize);
        lStream->Read((uint8_t*)lContents.data(),lSize);

        lParams.ti=new libtorrent::torrent_info(lContents.data(),lSize);
    }

//  lInfo.add_tracker("http://77.247.176.154:80/announce",0);
//  lInfo.add_tracker("http://trackeri2.rarbg.com/announce",0);
//  lInfo.add_tracker("http://tpb.tracker.prq.to:80/announce",0);

    mSession->async_add_torrent(lParams);
}

void CTorrentManager::OnAlertTimer()
{
    while(std::unique_ptr<libtorrent::alert> lAlert=mSession->pop_alert())
    {
        switch(lAlert->type())
        {
        case libtorrent::add_torrent_alert::alert_type:
            {
                libtorrent::add_torrent_alert *a=static_cast<libtorrent::add_torrent_alert*>(lAlert.get());
                msg_info() << "add torrent alert\n";
                mTorrents.push_back(new CTorrent(a->handle));
                break;
            }
        case libtorrent::portmap_alert::alert_type:
        case libtorrent::portmap_log_alert::alert_type:
        case libtorrent::peer_connect_alert::alert_type:
        case libtorrent::peer_blocked_alert::alert_type:
        case libtorrent::block_timeout_alert::alert_type:
        case libtorrent::peer_snubbed_alert::alert_type:
        case libtorrent::peer_unsnubbed_alert::alert_type:
        case libtorrent::peer_disconnected_alert::alert_type:
        case libtorrent::peer_ban_alert::alert_type:
        case libtorrent::block_finished_alert::alert_type:
        case libtorrent::block_downloading_alert::alert_type:
        case libtorrent::incoming_connection_alert::alert_type:
        case libtorrent::tracker_announce_alert::alert_type:
        case libtorrent::tracker_error_alert::alert_type:
        case libtorrent::tracker_reply_alert::alert_type:
        case libtorrent::dht_reply_alert::alert_type:
        case libtorrent::dht_announce_alert::alert_type:
        case libtorrent::dht_get_peers_alert::alert_type:
        case libtorrent::stats_alert::alert_type: // !!!!!!!!
        case libtorrent::piece_finished_alert::alert_type: // !!!!!!!!
        case libtorrent::hash_failed_alert::alert_type: // !!!!!!!!
            break;
        default:
            msg_info() << "unknown alert " << lAlert->type() << " " << lAlert->message() << "\n";
        }
    }
}

/*namespace bitdaemon*/ } /*namespace buola*/ }
